/* 模块：hash
 * 类型：函数单体
 * 功能：得到一个对象的 hash 值
 * 用法：
 * TODO ===========================================================
 * 
 */

#ifndef xpack_algo_hash
#define xpack_algo_hash
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::algo_hash::inc
#include"dumb/erase_type.hpp"
#include"instruction/ring_shift_left.hpp"
#include"instruction/ring_shift_right.hpp"
#include"instruction/count_of_set.hpp"
#include"interface/seqptr.hpp"
#include"interface/unified_seq.hpp"
#include"macro/xexport.hpp"
#include"macro/xmodify.hpp"
#include"memop/copy.hpp"
#include"memop/cast.hpp"
#include"meta/has_hash.hpp"
#include"meta/is_origin_string.hpp"
#include"meta/is_same.hpp"
#include"meta/item_of.hpp"
#include"meta/item_origin_of.hpp"
#include"meta/origin_of.hpp"
#include"meta/remove_const.hpp"
#include"meta/remove_ref.hpp"
#pragma pop_macro("xuser")

namespace mixc::algo_hash{
    template<class seq_t>
    inline u64 hash_core(seq_t const & seq, uxx seed = 0){
        auto x                      = u64(seed);
        auto y                      = u64(magic_number * (u32(-1) >> 1));

        for(uxx i = 0, length = inc::unified_seq<seq_t>{seq}->length; i < length; i++){
            using erase_type        = inc::erase_type<inc::remove_ref<decltype(seq[0])>>;
            auto total_bytes        = sizeof(erase_type);
            auto slice_bytes        = sizeof(u64) >= total_bytes ? total_bytes : sizeof(u64);
            auto mem_block          = erase_type{seq[i], not need_initial };
            auto offset             = 0;

            while(total_bytes != 0){
                auto temp           = u64{};

                // 大端模式存在字节序问题(尤其是当对象包含多个整数变量时)，如下方式无法通用化解决字节序问题
                for(uxx i = 0, shift = 0; i < slice_bytes; i++, shift += sizeof(u08) * 8){
                    temp           |= u64(u08p(xmodify(mem_block))[offset + i]) << shift;
                }

                x                  += inc::count_of_set(y + temp);
                temp               -= inc::count_of_set(x + y);
                y                  += inc::count_of_set(x + temp);
                x                  -= inc::ring_shift_left(y + temp, x);
                temp               += inc::ring_shift_right(x + y, temp);
                y                  -= inc::ring_shift_left(x + temp, y);
                total_bytes        -= slice_bytes;
                offset             += slice_bytes;

                if (total_bytes < slice_bytes){
                    slice_bytes     = total_bytes;
                }
            }
        }

        return y;
    }
}

namespace mixc::algo_hash::origin{
    template<class object_t>
    inline u64 hash64(object_t && object, uxx seed = 0){
        // 移除 const 和引用标记
        using obj_t         = inc::origin_of<object_t>;

        if constexpr (inc::is_origin_string<object_t &&>){
            using item_t    = inc::item_origin_of<obj_t>;
            return hash_core(inc::seqptr<item_t>(xforward(object)), seed);
        }
        else if constexpr (inc::can_unified_seqlize<obj_t>){
            return hash_core(inc::unified_seq<obj_t>(object), seed);
        }
        else if constexpr (inc::has_hash<obj_t>){
            return object.hash(seed);
        }
        else{
            return hash_core(inc::seqptr<obj_t>{ xmodify((obj_t &)object), 1/*length*/ }, seed);
        }
    }

    template<class object_t>
    inline u32 hash32(object_t && object, uxx seed = 0){
        auto value      = hash64(object, seed);
        auto high       = value >> 32;
        auto low        = value & 0xffffffff;
        auto result     = high + low;
        return result;
    }

    template<class object_t>
    inline u64 hash(object_t && object, uxx seed = 0){
        if constexpr (sizeof(uxx) == 4){
            return hash32(xforward(object), seed);
        }
        else{
            return hash64(xforward(object), seed);
        }
    }
}

#endif

xexport_space(mixc::algo_hash::origin)
